package minidb.transaction;

import minidb.buffer.Buffer;
import minidb.drive.FileOpException;
import minidb.drive.RecordOp;
import minidb.record.RID;
import minidb.record.Record;
import minidb.record.RecordPage;
import minidb.record.TableInfo;
import minidb.record.Tuple;
import static minidb.type.Const.*;

public class transaction {

	private java.util.ArrayList<String> tables;
	private long ID;
	public java.util.ArrayList<String> delete_tables;
	public int pageNum;
	private int pos;
	public boolean isRollBack = false;
	public transaction(java.util.ArrayList<String> t){
		this.tables = t;
		ID = Thread.currentThread().getId();
		this.delete_tables = new java.util.ArrayList<String>();
		this.pageNum = dbFile.allocPage();
		this.pos = INT_SIZE*4;
		TX.put(ID, this);
	}
	
	public void add(java.nio.ByteBuffer bt, int length){
		Buffer buf = bufpool.pin(pageNum);
		if(pos+length >= PAGE_SIZE){
			buf.putInt(FREE, pos);
			int newPage = dbFile.allocPage();
			buf.putInt(NEXT_PAGE, newPage);
			bufpool.unpin(buf);
			buf = bufpool.pin(newPage);
			buf.putInt(PRE_PAGE, pageNum);
			this.pageNum = newPage;
			this.pos = INT_SIZE * 4;
		}
		buf.putByte(pos, bt, length);
		pos+=length;
		bufpool.unpin(buf);
	}
	
	public void begin() throws FileOpException{
		for(int i = 0; i < tables.size(); i++){
			TableInfo tab = tablePage.get(tables.get(i));
			if(tab==null){
				String tableName = tables.get(i);
				i--;
				for(; i >= 0; i--){
					tab = tablePage.get(tables.get(i));
					synchronized(tab.lock){
						tab.ID = -1;
					}
				}
				throw new FileOpException("NO SUCH TABLE: " + tableName);
			}else{
				while(true){
					synchronized(tab.lock){
						if(tab.ID < 0){
							tab.ID = ID;
							break;
						}
					}
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	public void end(){
		for(int i = 0; i < tables.size(); i++){
			TableInfo tab = tablePage.get(tables.get(i));
			synchronized(tab.lock){
				tab.ID = -1;
			}
		}
		TX.remove(ID);
	}
	
	public void rollBack() throws FileOpException{
		Buffer buf = bufpool.pin(pageNum);
		int prePage = 0;
		this.isRollBack = true;
		while(true){
			pos -= INT_SIZE;
			if(pos < INT_SIZE * 4){
				prePage = buf.getInt(PRE_PAGE);
				if(prePage == 0){
					dbFile.deallocPage(buf.pageNum());
					break;
				}
				bufpool.unpin(buf);
				dbFile.deallocPage(buf.pageNum());
				buf = bufpool.pin(prePage);
				pos = buf.getInt(FREE);
				pos-=INT_SIZE;
			}
			int length = buf.getInt(pos);
			java.nio.ByteBuffer bt = java.nio.ByteBuffer.allocateDirect(length);
			buf.getByte(pos - length, bt, length);
			pos -= length + INT_SIZE;
			bt.rewind();
			length = bt.get();
			byte[] tmp = new byte[length];
			bt.get(tmp);
			String tabName = new String(tmp);
			int type = bt.get();
			if(type==1){
				new RecordOp(tabName).DeleteTuple(new RID(bt.getInt(), bt.getInt()).toString());
			}
			else if(type==2){
				TableInfo tab = tablePage.get(tabName);
				RecordPage rec = new RecordPage(tab);
				Record r = new Record(tab.recordlength(), rec.fieldinfo());
				byte []t = new byte[tab.recordlength()];
				String s_rid = new RID(bt.getInt(), bt.getInt()).toString();
				bt.get(t);
				r.record().rewind();
				r.record().put(t);
				new RecordOp(tabName).UpdateTuple(s_rid, new Tuple(r, tabName));
			}
			else if(type==3){
				TableInfo tab = tablePage.get(tabName);
				RecordPage rec = new RecordPage(tab);
				Record r = new Record(tab.recordlength(), rec.fieldinfo());
				byte []t = new byte[tab.recordlength()];
				bt.get(t);
				r.record().rewind();
				r.record().put(t);
				new RecordOp(tabName).InsertTuple(new Tuple(r, tabName));
			}
		}
		bufpool.unpin(buf);
		end();
	}
}
